#!/usr/bin/env bash

shopt -s globstar

image_pull() {
	local pull_url file dir
  pull_url="$1"
	dir="$2"
	file="${pull_url##*/}"

	curl -fSsL "$pull_url" > "$dir/rootfs.tar.gz"
}

image_build() {
	local build_options dir
	build_options="$1"
	dir="$2"

	[[ "$BUILDER_IMAGE" ]] || {
		BUILDER_IMAGE="alpine-builder"
		docker build -t "$BUILDER_IMAGE" builder
	}

	docker run -e "TRACE=$TRACE" --rm "$BUILDER_IMAGE" "${BUILD_OPTIONS[@]}" \
		> "$version_dir/rootfs.tar.xz"
}

build() {
	declare options_files="${*:-versions/**/options}"

	for file in $options_files; do
		( # shellcheck source=versions/gliderlabs-3.2/options
		source "$file"
		local version_dir
		version_dir="${file%/*}"
		arch="${ARCH:-x86_64}"
		: "${TAGS:?}"

		[[ "$PULL_URL" ]] && image_pull "$PULL_URL" "$version_dir"
		[[ "$BUILD_OPTIONS" ]] && image_build "${BUILD_OPTIONS[@]}" "$version_dir"

		# Build + tag images
		for tag in "${TAGS[@]}"; do
			docker build -t "$tag" "$version_dir"

			if [[ "$CIRCLE_BUILD_NUM" ]]; then
				{
					mkdir -p images \
					&& docker tag "$tag" "${tag}-${CIRCLE_BUILD_NUM}" \
					&& docker save "${tag}-${CIRCLE_BUILD_NUM}" \
						| xz -9e > "images/${tag//\//_}-${CIRCLE_BUILD_NUM}.tar.xz" \
					&& docker rmi "${tag}-${CIRCLE_BUILD_NUM}"
				} || true
			fi
		done )

	done
}

commit() {
	[[ "$CIRCLE_BRANCH" == "release" ]] || return 0

	declare options_files="${*:-versions/**/options}"
	local build_num="${CIRCLE_BUILD_NUM:-nobuild}"
	local current_branch
	current_branch=$(git rev-parse --abbrev-ref HEAD)
	: "${current_branch:?}"

	for file in $options_files; do
		local release version_dir
		version_dir="${file%/*}"
		release="${version_dir##versions/}"

		: "${release:?}" "${version_dir:?}"

		git checkout -B "rootfs/$release" "$current_branch"
		git add -f -- "$version_dir/rootfs.tar.*"
		git commit -m "release image version $release for build $build_num"
	done

	[[ "$NO_PUSH" ]] || git push -f origin 'refs/heads/rootfs/*'
	git checkout "$current_branch"
}

run_tests() {
	declare options_files="${*:-versions/**/options}"
	declare -a test_files
	for file in $options_files; do
		# shellcheck source=versions/gliderlabs-3.2/options
		source "$file"
		local tag
		tag="${TAGS[0]}" tag="${tag//:/-}" tag="${tag//\//_}"
		test_file="test/test_${tag}.bats"
		[[ -f "$test_file" ]] && test_files+=("$test_file") || echo " - skipping test for ${tag}: no test file ${test_file}"
	done

	[[ "${test_files[@]}" ]] && bats "${test_files[@]}"
}

push() {
	[[ "$CIRCLE_BRANCH" == "release" ]] || return 0
	[[ "$NO_PUSH" ]] && return 0

	declare options_files="${*:-versions/**/options}"
	for file in $options_files; do
		( #shellcheck source=versions/gliderlabs-3.2/options
		source "$file"
		for tag in "${TAGS[@]}"; do
			if docker history "$tag" &> /dev/null; then
				[[ "$PUSH_IMAGE" ]] && docker push "$tag"
			fi
		done
		exit 0 )
	done
}

library() {
	convert() {
		# takes a space-separated list of alpine-linux architectures, and returns a space-separated list of docker architectures
		local i=0
		for arch in "$@"; do
			case "$arch" in # converts Alpine Linux arch strings to Docker arch strings
				x86_64) echo -n "amd64";;
				x86) echo -n "i386";;
				armhf) echo -n "arm32v6";; # arm32v7 is not officially supported by Alpine, but arm32v6 should still work.
				aarch64) echo -n "arm64v8";;
				ppc64le) echo -n "ppc64le";;
				s390x) echo -n "s390x";;
				*) echo >&2 "error: unknown arch '$arch'"; return 1;; # Fail on unknown archs
			esac
			if [[ i -ne $#-1 ]]; then
				echo -n " "
			fi
			let i=$i+1
		done
	}

	local release_refs
	release_refs="$(git ls-remote --exit-code --heads origin release)"
	: "${release_refs:?}"

	echo
	echo "# autogenerated by https://github.com/gliderlabs/docker-alpine/blob/master/build"
	echo
	echo "Maintainers: Glider Labs <team@gliderlabs.com> (@gliderlabs)"
	echo "GitRepo: https://github.com/gliderlabs/docker-alpine.git"
	echo "GitFetch: refs/heads/release"
	echo "GitCommit: ${release_refs:0:40}"

	for file in versions/library-*/x86_64/options versions/library-*/options; do
		# shellcheck source=versions/library-3.2/options
		source "$file"
		local version_dir release tags
		version_dir="${file%/options}"
		version_dir="${version_dir%/x86_64}"
		release="${version_dir#versions/}"
		tags="${TAGS[*]#alpine:}"
		if [ -e "$version_dir/options" ]; then
			# compatability with older configs that were only written for x86_64 architectures
			ARCHS=("x86_64")
		else
			ARCHS=( "$version_dir"/*/options )
			ARCHS=( "${ARCHS[@]#$version_dir/}" )
			ARCHS=( "${ARCHS[@]%/*}" )
		fi
		a="$(convert "${ARCHS[@]}")"

		echo
		echo "Tags: ${tags// /, }"
		echo "Architectures: ${a// /, }"

		local i=0
		for arch in $a; do
			# ${ARCHS[i]} is the alpine version string, different from $archs, the docker version string.
			# Our folder structure is based on the alpine version string
			local branch dir refs
			if [ -e "$version_dir/options" ]; then
				# compatability with older configs that were only written for x86_64 architectures
				branch="rootfs/${release}"
				dir="$version_dir"
			else
				branch="rootfs/${release}/${ARCHS[i]}"
				dir="$version_dir/${ARCHS[i]}"
			fi
			refs="$(git ls-remote --exit-code --heads origin "$branch")"
			: "${refs:?}"
			echo "$arch-GitFetch: refs/heads/$branch"
			echo "$arch-GitCommit: ${refs:0:40}"
			echo "$arch-Directory: $dir"
			let i=i+1
		done
	done
	echo
}

main() {
	set -eo pipefail; [[ "$TRACE" ]] && set -x
	declare cmd="$1"
	case "$cmd" in
		test)	shift;	run_tests "$@";;
		commit)	shift;	commit "$@";;
		push)	shift;	push "$@";;
		library) shift; library;;
		*)		build "$@";;
	esac
}

main "$@"
